package com.ala4.oxcafe.common.utils;

import com.ala4.oxcafe.properties.OssProperties;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.common.auth.HmacSHA1Signature;
import com.aliyun.oss.common.utils.DateUtil;
import com.aliyun.oss.dto.OSSPutDTO;
import com.aliyun.oss.dto.OSSUrlDTO;
import com.aliyun.oss.internal.OSSConstants;
import com.aliyun.oss.internal.SignUtils;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditionsA;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;

/**
 * OSS工具类
 *
 * @author PING
 * @date 2024/1/10 21:31
 */
@Slf4j
@Component
public class OssClientUtil {

    private static OssProperties ossProperties;

    /**
     * 获取上传签名参数
     *
     * @param key 文件名称-设置上传到OSS文件的前缀，可置空此项。置空后，文件将上传至Bucket的根目录下。
     * @return 签名DTO
     */
    public static OSSPutDTO policy(String key) {

        // 3.指定默认超时时间是100s
        // ossProperties.getUploadExpireTime()

        // 4.得到最终的截止时间
        long expireEndTime = System.currentTimeMillis() + ossProperties.getUploadExpireTime() * 1000;
        Date expiration = new Date(expireEndTime);
        // 5.封装签名
        PolicyConditionsA policyConditions = new PolicyConditionsA();
        // 5.1 设置可上传文件的大小，这里设置为 0 - 10MB
        policyConditions.addConditionItem(OSSConstants.CONTENT_LENGTH_RANGE, 0, 10485760);
        // 5.2 设置上传文件的前缀、可忽略
        policyConditions.addConditionItem(MatchMode.StartWith, OSSConstants.KEY, key);

        // 5.3 对Policy签名后的字符串。
        String postPolicy = generatePostPolicy(expiration, policyConditions);

        String postSignature = calculatePostSignature(postPolicy, ossProperties.getAccessKeySecret());

        // 6.用户表单上传的策略（Policy），Policy为经过Base64编码过的字符串。
        byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
        String encodedPolicy = new String(Base64.getEncoder().encode(binaryData));
        // 7.封装签名直传服务返回给客户端消息Body内容
        OSSPutDTO ossPutDTO = new OSSPutDTO();
        // 7.1 用户请求的AccessKey ID。
        ossPutDTO.setOssaccessKeyId(ossProperties.getAccessKeyId());
        // 7.2 用户表单上传的策略（Policy），Policy为经过Base64编码过的字符串。
        ossPutDTO.setPolicy(encodedPolicy);
        // 7.3 对Policy签名后的字符串。
        ossPutDTO.setSignature(postSignature);
        // 7.4 限制上传的文件前缀。
        ossPutDTO.setKey(key);
        // 7.5 用户发送上传请求的域名。
        ossPutDTO.setHost("https://" + ossProperties.getBucket() + "." + ossProperties.getEndpoint());
        // 7.6 由服务器端指定的Policy过期时间，格式为Unix时间戳（自UTC时间1970年01月01号开始的秒数）。
        ossPutDTO.setExpire(String.valueOf(expireEndTime / 1000));
        return ossPutDTO;
    }

    /**
     * 获取签名的URL
     *
     * @return URL
     */
    public static OSSUrlDTO policyUrl(String pathFileName) {
        long expireEndTime = System.currentTimeMillis() + ossProperties.getDownloadExpireTime() * 1000;
        Date expiration = new Date(expireEndTime);
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
        String urlStr = generatePresidedUrl(ossProperties.getBucket(), pathFileName, expiration);
        return new OSSUrlDTO(urlStr, String.valueOf(expireEndTime / 1000));
    }

    /**
     * 获取签名字符串
     *
     * @param expiration 时间
     * @param conds      签名字段
     * @return 签名字符串
     */
    private static String generatePostPolicy(Date expiration, PolicyConditionsA conds) {
        String formatedExpiration = DateUtil.formatIso8601Date(expiration);
        String jsonizedExpiration = String.format("\"expiration\":\"%s\"", formatedExpiration);
        String jsonizedConds = conds.jsonize();

        return String.format("{%s,%s}", jsonizedExpiration, jsonizedConds);
    }

    private static String calculatePostSignature(String postPolicy, String secretAccessKey) {
        byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
        String encPolicy = new String(Base64.getEncoder().encode(binaryData));
        return new HmacSHA1Signature().computeSignature(secretAccessKey, encPolicy);
    }

    /**
     * 获取私有访问URL
     *
     * @param bucketName 桶名称
     * @param key        文件路径
     * @param expiration 超时时间
     * @return
     */
    private static String generatePresidedUrl(String bucketName, String key, Date expiration) {
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, key);
        request.setExpiration(expiration);
        request.setMethod(HttpMethod.GET);
        return SignUtils.buildSignedURL(request, ossProperties);
    }

    @Autowired
    public void setOssProperties(OssProperties ossProperties) {
        OssClientUtil.ossProperties = ossProperties;
    }
}
